package matrix;

public class Jacobi extends SSLE implements Iterative {

	int itr = -1;
	double prec = 0;

	@Override
	protected void solve(boolean buildSteps) throws Exception {
		Matrix g = a.augment(b);
		if (buildSteps) {
			steps.add("[A|B]");
			steps.add(g.toLatex(new int[] { a.width() }));
		}
		// dominant
		if (buildSteps)
			steps.add("Making \\;\\; diagonal \\;\\; dominant");
		try {
			makeDiagDominant(g);
		} catch (ArrayIndexOutOfBoundsException e) {
			steps.add("Failed. Can't \\;\\; solve");
			throw new Exception("Can't solve");
		}
		if (buildSteps)
			steps.add(g.toLatex(new int[] { a.width() }));
		// equations
		if (buildSteps) {
			steps.add("Iteration \\;\\; equations");
			for (int i = 0; i < g.height(); i++) {
				steps.add("x_" + (i + 1) + "(k+1)={" + g.get(g.width() - 1, i) + "-");
				for (int j = 0; j < g.height(); j++) {
					if (i != j)
						steps.set(steps.size() - 1, steps.get(steps.size() - 1) + g.get(j, i).doubleValue() + "*x_" + (j + 1) + "(k)-");
				}
				steps.set(steps.size() - 1, steps.get(steps.size() - 1).substring(0, steps.get(steps.size() - 1).length() - 1));
				steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "\\over" + g.get(i, i).doubleValue() + "}");
			}
			steps.add("Iterations");
			steps.add("\\left|\\begin{array}{c|");
			for (int i = 0; i < g.height(); i++)
				steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "c");
			steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "}");
			// init
			steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "0");
			for (int i = 0; i < x.height(); i++)
				steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "&" + x.get(0, i));
			steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "\\\\");
		}
		// iterations
		for (int i = 0; i < itr; i++) {
			x = step(g);
			if (buildSteps) {
				steps.set(steps.size() - 1, steps.get(steps.size() - 1) + (i + 1));
				for (int j = 0; j < x.height(); j++)
					steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "&" + x.get(0, j));
				steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "\\\\");
			}
		}
		// done
		if (buildSteps) {
			steps.set(steps.size() - 1, steps.get(steps.size() - 1) + "\\end{array}\\right|");
			steps.add("Final \\;\\; solution");
			steps.add("X=" + x.toLatex());
		}
	}

	private Matrix step(Matrix g) {
		Matrix newX = new Matrix(x.width(), x.height(), x.type());
		for (int i = 0; i < g.height(); i++) {
			newX.set(0, i, g.get(g.width() - 1, i));
			for (int j = 0; j < g.width() - 1; j++)
				if (i != j)
					newX.set(0, i, newX.get(0, i).doubleValue() - x.get(0, j).doubleValue() * g.get(j, i).doubleValue());
			newX.set(0, i, newX.get(0, i).doubleValue() / g.get(i, i).doubleValue());
		}
		return newX;
	}

	private void makeDiagDominant(Matrix g) {
		for (int y = 0; y < g.height(); y++)
			g.swapRow(y, maxCol(g, y));
	}

	private int maxCol(Matrix m, int y) {
		double sum = 0;
		for (int x = 0; x < m.width() - 1; x++)
			sum += Math.abs(m.get(x, y).doubleValue());
		for (int x = 0; x < m.width() - 1; x++)
			if (2 * Math.abs(m.get(x, y).doubleValue()) > sum)
				return x;
		return -1;
	}

	@Override
	public String nSteps() {
		return itr + " iterations";
	}

	@Override
	public String toString() {
		return "Jacobi";
	}

	@Override
	public void set(Matrix x, int itr, double prec) {
		this.x = x;
		this.itr = itr;
		this.prec = prec;
	}

}
